package mips.instructions;

import mips.Main;

/**
 * <code>BLTZALL</code> instruction<br/>
 * Branch On Less Than Zero And Link Likely</br>
 * @author jnmartin84@gmail.com
 */
public class BLTZALL extends Instruction {

	private static final BLTZALL INSTANCE = new BLTZALL();
	private static final String INSTRUCTION_NAME = "BLTZALL";

	private BLTZALL(){}

	public static final BLTZALL getInstance() {
		return INSTANCE;
	}

	/**
	 * <b>Format:</b><br/>
	 * BLTZALL rs, offset<br/><br/>
	 * <b>Description:</b><br/>
	 * A branch target address is computed from the sum of the address of the<br/>
	 * instruction in the delay slot and the 16-bit offset, shifted left two bits and<br/>
	 * sign-extended. Unconditionally, the address of the instruction after the<br/>
	 * delay slot is placed in the link register, r31. If the contents of general<br/>
	 * register rs have the sign bit set, then the program branches to the target<br/>
	 * address, with a delay of one instruction.<br/><br/>
	 * General register rs may not be general register 31, because such an<br/>
	 * instruction is not restartable. An attempt to execute this instruction with<br/>
	 * register 31 specified as rs is not trapped, however. If the conditional<br/>
	 * branch is not taken, the instruction in the branch delay slot is nullified.<br/><br/>
	 * <b>Operation:</b><br/>
	 * T: target &larr; (offset<sub>15</sub>)<sup>14</sup> || offset || 0<sup>2</sup><br/>
	 * condition &larr; (GPR[rs]<sub>31</sub> = 1)<br/>
	 * GPR[31] &larr; PC + 8<br/>
	 * T+1: if condition then<br/>
	 * PC &larr; PC + target<br/>
	 * else<br/>
	 * NullifyCurrentInstruction<br/>
	 * endif<br/>
	 */
	@Override
	public final void execute(final int instruction) {
		
		final int rs = (instruction >> 21) & 0x0000001F;
		final int offset = Instruction.signExtendH(instruction & 0x0000FFFF) << 2;

		mips.R4300i.GPR[31] = mips.R4300i.nPC + 8;
		
		if ((mips.R4300i.GPR[rs] & 0x80000000) != 0) {

			mips.R4300i.PC = mips.R4300i.nPC;
			mips.R4300i.nPC = mips.R4300i.PC + offset;
			
			if(Main.tracing) {
				mips.R4300i.targets.put(mips.R4300i.nPC,null);
			}
		} 
		else {

			mips.R4300i.PC = mips.R4300i.nPC + 4;
			mips.R4300i.nPC = mips.R4300i.PC + 4;
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String emit(final int instruction) {
		
		final int rs = (instruction >> 21) & 0x0000001F;
		final int offset = Instruction.signExtendH(instruction & 0x0000FFFF) << 2;
		
		return	"		mips.CPU.GPR[31] = mips.CPU.nPC + 8;\n" + 
				"		\n" + 
				"		if ((mips.CPU.GPR["+rs+"] & 0x80000000) != 0) {\n" + 
				"			\n" + 
				"			mips.CPU.PC = mips.CPU.nPC;\n" + 
				"			mips.CPU.nPC = mips.CPU.PC + "+offset+";\n" + 
				"		} \n" + 
				"		else {\n" + 
				"			\n" + 
				"			mips.CPU.PC = mips.CPU.nPC + 4;\n" + 
				"			mips.CPU.nPC = mips.CPU.PC + 4;\n" +
				"			skipDelaySlot = true;\n" +				
				"		}\n";
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName(final int instruction) {
		return getName();
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName() {
		return INSTRUCTION_NAME;
	}
}